﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Numbers;
using Numbers.Transactions;

namespace System8Equations
{
    /// <summary>
    /// Deals with a single set of system 8 equations -- all held together in the proper way, and flexible
    /// so you can modify one bit an it should correctly propagate to the others.
    /// </summary>
    public class S8Equations
    {
        /// <summary>
        /// Fired when some value changes internally.
        /// </summary>
        public event Action ValuesUpdated;

        /// <summary>
        /// All the samples we have to deal with in system 8.
        /// </summary>
        public FlavorSample n { get; private set; }
        public FlavorSample p{ get; private set; }
        public FlavorSample n1{ get; private set; }
        public FlavorSample p1{ get; private set; }
        public FlavorSample n2{ get; private set; }
        public FlavorSample p2{ get; private set; }
        public FlavorSample n12{ get; private set; }
        public FlavorSample p12{ get; private set; }

        /// <summary>
        /// The tagging efficiency on the n and p sample for the life time tagger.
        /// Normally this would be equal.
        /// </summary>
        public TagEfficiency eff_LT_n { get; private set; }
        public TagEfficiency eff_LT_p { get; private set; }

        public epsilon_12 eff_LT_n_cl { get; private set; }
        public epsilon_12 eff_LT_p_cl { get; private set; }

        /// <summary>
        /// Tag eff of the soft muon tagger on the n and p samples
        /// </summary>
        public TagEfficiency eff_SMT_n { get; private set; }
        public TagEfficiency eff_SMT_p { get; private set; }

        public epsilon_12 eff_SMT_n_cl { get; private set; }
        public epsilon_12 eff_SMT_p_cl { get; private set; }

        /// <summary>
        /// Tag eff of the double tagging on the n and p samples.
        /// </summary>
        public TagEfficiency eff_SMTLT_n { get; private set; }
        public TagEfficiency eff_SMTLT_p { get; private set; }

        public epsilon_12 eff_SMTLT_n_cl { get; private set; }
        public epsilon_12 eff_SMTLT_p_cl { get; private set; }

        /// <summary>
        /// This is the tagging efficiency of the away tagger - it isn't really part of system 8, but
        /// helps connect everything together.
        /// </summary>
        public TagEfficiency eff_away { get; private set; }
        public epsilon_12 eff_away_cl { get; private set; }

        /// <summary>
        /// How different are the tag rates on the p/n samples for the SMT tagger?
        /// </summary>
        public Alpha alpha_SMT_p_n { get; private set; }
        public Alpha alpha_LT_p_n { get; private set; }

        public Alpha_12 alpha_SMT_p_n_cl { get; private set; }
        public Alpha_12 alpha_LT_p_n_cl { get; private set; }

        /// <summary>
        /// Double tag - and how we get from one to the other
        /// </summary>
        public Alpha alpha_SMTLT_p { get; private set; }
        public Alpha alpha_SMTLT_n { get; private set; }

        private epsilon_12 eff_SMTGivenLT_n_cl { get; set; }
        private epsilon_12 eff_SMTGivenLT_p_cl { get; set; }

        public Alpha_12 alpha_SMTLT_p_cl { get; private set; }
        public Alpha_12 alpha_SMTLT_n_cl { get; private set; }

        /// <summary>
        /// The eps for the smt and lt taggers on the two samples.
        /// </summary>
        public epsilon_12 epscl_SMT_p{ get; private set; }
        public epsilon_12 epscl_SMT_n { get; private set; }
        public epsilon_12 epscl_LT_p { get; private set; }
        public epsilon_12 epscl_LT_n { get; private set; }

        /// <summary>
        /// Finall, the results of this calculation
        /// </summary>
        public System8UpdatingResults results { get; private set; }

        public S8Equations()
        {
            n = new FlavorSample();
            p = new FlavorSample();
            n1 = new FlavorSample();
            n2 = new FlavorSample();
            p1 = new FlavorSample();
            p2 = new FlavorSample();
            n12 = new FlavorSample();
            p12 = new FlavorSample();

            eff_away = new TagEfficiency();
            eff_LT_n = new TagEfficiency();
            eff_LT_p = new TagEfficiency();
            eff_SMTLT_n = new TagEfficiency();
            eff_SMTLT_p = new TagEfficiency();
            eff_SMT_n = new TagEfficiency();
            eff_SMT_p = new TagEfficiency();

            eff_away_cl = new epsilon_12 (p, n);
            eff_LT_n_cl = new epsilon_12(n1, n);
            eff_LT_p_cl = new epsilon_12(p1, p);
            eff_SMTLT_n_cl = new epsilon_12(n12, n);
            eff_SMTLT_p_cl = new epsilon_12(p12, p);
            eff_SMT_n_cl = new epsilon_12(n2, n);
            eff_SMT_p_cl = new epsilon_12(p2, p);

            alpha_LT_p_n = new Alpha();
            alpha_SMT_p_n = new Alpha();
            alpha_SMTLT_p = new Alpha();
            alpha_SMTLT_n = new Alpha();

            alpha_LT_p_n_cl = new Alpha_12(eff_LT_p_cl, eff_LT_n_cl);
            alpha_SMT_p_n_cl = new Alpha_12(eff_SMT_p_cl, eff_SMT_n_cl);

            ///
            /// Hook everyone up to listen for changes. Because of the way this works, we only have ot listen to the raw numbers
            /// if anything else changes, so will the raw numbers! :-)
            /// 

            n.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            n1.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            n2.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            n12.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            p.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            p1.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            p2.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));
            p12.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler((o, a) => AValueChanged(o));

            ///
            /// The double-tag cl's require some harder calculations - so we have to build
            /// up some extra eff's to get them right. Just build them right here...
            /// 

            eff_SMTGivenLT_n_cl = new epsilon_12(n12, n2);
            eff_SMTGivenLT_p_cl = new epsilon_12(p12, p2);

            alpha_SMTLT_n_cl = new Alpha_12(eff_SMTGivenLT_n_cl, eff_SMT_n_cl);
            alpha_SMTLT_p_cl = new Alpha_12(eff_SMTGivenLT_p_cl, eff_SMT_p_cl);

            ///
            /// And the system 8 results
            /// 

            results = new System8UpdatingResults(
                n.TotalValue, p.TotalValue,
                n1.TotalValue, n2.TotalValue,
                p1.TotalValue, p2.TotalValue,
                n12.TotalValue, p12.TotalValue);

            ///
            /// Connect everyone together!
            /// 

            FlavorSample.Multiply(p, eff_away, n); // p = eff_away * n

            FlavorSample.Multiply(n1, eff_LT_n, n); // n1 = eff_LT_n * n
            FlavorSample.Multiply(n2, eff_SMT_n, n); // n2 = eff_SMT_n * n

            TagEfficiency.Multiply(eff_LT_p, alpha_LT_p_n, eff_LT_n); // eff_lt_p = alpha_lt * eff_lt_n
            TagEfficiency.Multiply(eff_SMT_p, alpha_SMT_p_n, eff_SMT_n); // eff_smt_p = altpha_lt * eff_smt_n

            FlavorSample.Multiply(p1, eff_LT_p, p); // n1 = eff_LT_n * n
            FlavorSample.Multiply(p2, eff_SMT_p, p); // n2 = eff_SMT_n * n

            TagEfficiency.Multiply(eff_SMTLT_n, alpha_SMTLT_n, eff_SMT_n, eff_LT_n);
            TagEfficiency.Multiply(eff_SMTLT_p, alpha_SMTLT_p, eff_SMT_p, eff_LT_p);

            FlavorSample.Multiply(n12, eff_SMTLT_n, n); // n12 = eff_LTSMT_n * n
            FlavorSample.Multiply(p12, eff_SMTLT_p, p); // n12 = eff_LTSMT_n * n
        }

        /// <summary>
        /// Copy the values over so that the two objects look alike
        /// </summary>
        /// <param name="value"></param>
        public void Copy(S8Equations v)
        {
            n.Bottom = v.n.Bottom;
            n.Charm = v.n.Charm;
            n.Light = v.n.Light;
            n1.Bottom = v.n1.Bottom;
            n1.Charm = v.n1.Charm;
            n1.Light = v.n1.Light;
            n2.Bottom = v.n2.Bottom;
            n2.Charm = v.n2.Charm;
            n2.Light = v.n2.Light;
            n12.Bottom = v.n12.Bottom;
            n12.Charm = v.n12.Charm;
            n12.Light = v.n12.Light;

            p.Bottom = v.p.Bottom;
            p.Charm = v.p.Charm;
            p.Light = v.p.Light;
            p1.Bottom = v.p1.Bottom;
            p1.Charm = v.p1.Charm;
            p1.Light = v.p1.Light;
            p2.Bottom = v.p2.Bottom;
            p2.Charm = v.p2.Charm;
            p2.Light = v.p2.Light;
            p12.Bottom = v.p12.Bottom;
            p12.Charm = v.p12.Charm;
            p12.Light = v.p12.Light;
        }

        /// <summary>
        /// Some value has changed. We really only want to record the fact that a single
        /// value has changed - even though there will be lots of cascades. So we need to
        /// sign up to watch for the cascade to have finished.
        /// We do this by finding the transaction and signing up to be notified.
        /// </summary>
        /// <returns></returns>
        private void AValueChanged(object sender)
        {
            FlavorSample fs = sender as FlavorSample;
            fs.TotalValue.TransactionManager.SignupForTransactionDone(this, () => OnValuesUpdated());
        }

        /// <summary>
        /// Internal routine to fire off the event if anyone is listening.
        /// </summary>
        private void OnValuesUpdated()
        {
            if (ValuesUpdated != null)
                ValuesUpdated();
        }
    }
}
